/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package gui.multimedia;

import explorer.FileModel;
import explorer.FileRenderer;
import explorer.FileTreeModel;
import gui.AbstractModule;
import gui.MainWindow;
import gui.graphicals.BannerLabel;
import gui.graphicals.ToolBar;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.*;
import javax.swing.JMenuItem;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.plaf.basic.BasicBorders.ButtonBorder;
import javax.swing.tree.TreeSelectionModel;
import multimedia.AudioPlaylistController;
import multimedia.AudioPlaylistModel;
import multimedia.AudioSongModel;
import multimedia.MultimediaController;
import multimedia.MultimediaModel;

/**
 *
 * @author thomas
 */
public class MultimediaView extends AbstractModule implements ActionListener, MouseListener, TreeSelectionListener {
    
    private ToolBar _toolBar;
    private Thread _toolBarThread;
    private boolean _isToolBarUpdate = false;
    
    private BannerLabel _lblSong;
    
    private JButton _btnPlay;
    private JButton _btnShuffle;
    private JButton _btnRepeat;
    
    private JLabel _lblTime;
    private JSlider _slideTime;
    
    private MultimediaSongList _lstSongs;
    private JComboBox _cmbPlaylists;
    private JTree _myTree;
    
    private JLabel _lblInfos;
    
    private MultimediaRightClickPopupMenu _rightClick;
    
    private MultimediaController _myController;

    public MultimediaView(MainWindow mainWindow) {
        super(mainWindow); 
        updateAll();
    }

    @Override
    protected void initController() {
        try {
            _myController = MultimediaController.getInstance(this);
        } catch (FileNotFoundException ex) {
            showFileNotFoundException(MultimediaController.getMusicDirectory());
        }
    }
    
    @Override
    protected void initView() {
        
        _lstSongs = new MultimediaSongList(this);
        _rightClick = new MultimediaRightClickPopupMenu(this);
        
    }

    @Override
    protected ToolBar getToolBar() {

        _toolBar = new ToolBar();
        ActionListener al = this;

        _lblSong = new BannerLabel("Aucune chanson en cours", 50);
        _lblSong.setSize(200, 20);
        _lblSong.setPreferredSize(new Dimension(200, 20));
        _lblSong.setMinimumSize(new Dimension(200, 20));
        _lblSong.setMaximumSize(new Dimension(200, 20));
        _lblSong.start();
        
        _toolBar.add(_lblSong);
        
        _toolBar.addSeparator();
        
        _toolBar.makeButton("btn_previous.png",
                "PREVIOUS",
                "Aller à la chanson précédente",
                "PREVIOUS",
                al);

        _btnPlay = _toolBar.makeNewButton("btn_play.png",
                "PLAY",
                "Jouer / Mettre sur pause",
                "PLAY",
                al);
        _toolBar.add(_btnPlay);

        _toolBar.makeButton("btn_stop.png",
                "STOP",
                "Arrêter la lecture",
                "STOP",
                al);

        _toolBar.makeButton("btn_next.png",
                "NEXT",
                "Aller à la chanson suivante",
                "NEXT",
                al);

        _toolBar.addSeparator();

        _btnShuffle = _toolBar.makeNewButton("btn_shuffle.png",
                "SHUFFLE",
                "Passer en lecture aléatoire",
                "SHUFFLE",
                al);
        _toolBar.add(_btnShuffle);

        _btnRepeat = _toolBar.makeNewButton("btn_repeat.png",
                "REPEAT",
                "Répèter la liste de lecture",
                "REPEAT",
                al);
        _toolBar.add(_btnRepeat);

        _toolBar.addSeparator();

        _slideTime = new JSlider();
        _slideTime.setValue(0);
        _slideTime.setMaximum(100);
        _lblTime = new JLabel("0:00 / 0:00");

        _toolBar.add(_slideTime);
        _toolBar.add(_lblTime);

        _toolBarThread = new Thread() {

            @Override
            public void run() {

                while (true) {

                    // If player is stopped, we stop the thread
                    if (!_isToolBarUpdate) {
                        synchronized (_toolBarThread) {
                            try {
                                _toolBarThread.wait();
                            } catch (InterruptedException ex) {
                                Logger.getLogger(MultimediaView.class.getName()).log(Level.SEVERE, null, ex);
                            }
                        }
                    }
                    
                    // Verify if play button need to be update
                    if (!_myController.getModel().getState().equalsIgnoreCase(_btnPlay.getActionCommand())) {
                        updateToolBar();
                    }

                    // Update Labels and JSlide value
                    try {
                        AudioSongModel asm = _myController.getActivePlaylist().getActiveSongController().getModel();

                        int time = asm.getDuration();
                        int timeTotal = asm.getTotalDuration();

                        _lblTime.setText(_myController.timeToString(time) + " / " + _myController.timeToString(timeTotal));
                        _slideTime.setValue(time * 100 / timeTotal);
                        
                        if (!_lblSong.getText().equals(asm.toString())) {
                            _lblSong.setAnotherText(asm.toString());
                        }

                    } catch (NullPointerException ex) {
                    }

                    // Thread sleep to avoid processus time utilisation
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(MultimediaView.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        };

        return _toolBar;
    }

    /**
     * Set the slide and label with song duration to 0
     */
    public void reinitializeToolBar() {
        _lblTime.setText("0:00 / 0:00");
        _slideTime.setValue(0);
        _slideTime.setMaximum(100);
    }
    
    /**
     * Update the toolbar to display a refresh information
     */
    public void updateToolBar() {

        MultimediaModel model = _myController.getModel();
        
        String state = model.getState();

//        System.out.println("UPDATE TOOLBAR --> " + state);
        
        // Toggle play/pause button
        if (state.equalsIgnoreCase("PLAY")) {
            
            _toolBar.changeButton(_btnPlay, "btn_pause.png", "PAUSE", "PAUSE");

            // Start thread to update toolbar if it did not launch before
            if (!_toolBarThread.isAlive()) {
                _toolBarThread.start();
            }

            // Launch again thread
            _isToolBarUpdate = true;
            synchronized (_toolBarThread) {
                _toolBarThread.notify();
            }


        } else if (state.equalsIgnoreCase("PAUSE") || state.equalsIgnoreCase("STOP")) {

            _toolBar.changeButton(_btnPlay, "btn_play.png", "PLAY", "PLAY");
            _isToolBarUpdate = false;
        }
        
        // Toggle shuffle button
        if (model.isShuffle()) {
            _btnShuffle.setBorder(new ButtonBorder(Color.RED, Color.RED, Color.RED, Color.RED));
            //_btnShuffle.setBackground(Color.RED);
        } else {
            _btnShuffle.setBorder(null);
            //_btnShuffle.setBackground(Color.LIGHT_GRAY);
        }
        
        // Toggle repeat button
        if (model.isRepeat()) {
            _btnRepeat.setBorder(new ButtonBorder(Color.RED, Color.RED, Color.RED, Color.RED));
        } else {
            _btnRepeat.setBorder(null);
        }
    }

    @Override
    protected JComponent getMainFrame() {

        JSplitPane myView = new JSplitPane();

        myView.setLeftComponent(buildMusicLibrary());
        myView.setRightComponent(buildPlayListView());
        myView.setDividerLocation(500);

        return myView;
    }

    /**
     * Build the music library tree and return its scrollpane
     * @return JScrollpane who contains the file tree
     */
    private JScrollPane buildMusicLibrary() {

//        myView.add(new JLabel("Ma bibliothèque"));
        File root = new File(MultimediaController.getMusicDirectory());
        FileTreeModel model = new FileTreeModel(root);
        
        model.showFile(true);
        model.addExtension(".mp3");
        model.addExtension(".ogg");
        _myTree = new JTree(model);

        _myTree.setCellRenderer(new FileRenderer());
        _myTree.addTreeSelectionListener(this);
        _myTree.addMouseListener(this);
        _myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);

        return new JScrollPane(_myTree);
    }

    /**
     * Build the playlist view and return it
     * @return JPanel who contains playlists combobox and songs list
     */
    private JComponent buildPlayListView() {

        JPanel myView = new JPanel();
        myView.setLayout(new BorderLayout());

        _cmbPlaylists = new JComboBox();
        _cmbPlaylists.addActionListener(this);

        _lblInfos = new JLabel();
        //_lblInfos.set
        _lblInfos.setText(""
                + "<html>"
                + "Titre :<br>"
                + "Artiste :<br>"
                + "Album :<br>"
                + "Date :<br>"
                + "Chemin :<br>"
                + "Durée :<br>"
                + "</html>");
        
        myView.add(_cmbPlaylists, BorderLayout.NORTH);
        myView.add(_lstSongs, BorderLayout.CENTER);
        myView.add(_lblInfos, BorderLayout.SOUTH);
        
        return myView;
    }

    /**
     * Update the combo box of playlists
     */
    public void updatePlaylistCombo() {

        //System.out.println("Update Playlists Combobox");
        // Load all playlists
        ArrayList<AudioPlaylistController> playLists = _myController.getPlaylists();

        if (playLists == null) {
            return;
        }

        // Add each playlists to the combobox
        _cmbPlaylists.removeAllItems();
        for (AudioPlaylistController el : playLists) {
            System.out.println(el.getModel().getName() + "(" + playLists.size() + ")");
            _cmbPlaylists.addItem(el.getModel());
        }
    }

    /**
     * Update the list of songs
     */
    public void updateSongsList() {

        // Search selected playlist
        AudioPlaylistModel selectedItem = (AudioPlaylistModel) _cmbPlaylists.getSelectedItem();
        if (selectedItem == null) {
            return;
        }

        // Set a new model to show the song list
        _lstSongs.update(_myController.getActivePlaylist().getModel());
    }
    
    /**
     * Update the details about the played song
     */
    public void updateCurrentSong() {
        
        AudioSongModel asm = null;
        try {
            asm = _myController.getActivePlaylist().getActiveSongController().getModel();
        } catch (NullPointerException ex) {
            return;
        }
        
        if (asm != null) {
            _lblInfos.setText(""
                + "<html>"
                + "Titre : " + asm.getTitle() + "<br>"
                + "Artiste : " + asm.getArtist() + "<br>"
                + "Album : " + asm.getAlbum() + "<br>"
                + "Date : " + asm.getDate() + "<br>"
                + "Chemin : " + asm.getPath() + "<br>"
                + "Durée : " + _myController.timeToString(asm.getTotalDuration()) + "<br>"
                + "</html>");
        }
    }

    /**
     * Update all
     * @see updatePlaylistCombo
     * @see updateSongsList
     */
    public final void updateAll() {

        updateToolBar();
        updatePlaylistCombo();
        updateSongsList();
        updateCurrentSong();
        
    }
    
    public void launchNewSong(String path) {
        
        try {
            _myController.listenToSong(path);
            updateAll();

        } catch (FileNotFoundException ex) {
            this.showFileNotFoundException(path);
        } catch (IOException ex) {
            this.showDevErrorBox(ex);
        } catch (UnsupportedAudioFileException ex) {
            this.showDevErrorBox(ex);
        }
    }

    @Override
    public void actionPerformed(ActionEvent ae) {

        // Close right click menu if source if an JMenuItem
        if (ae.getSource().getClass() == JMenuItem.class) {
            _rightClick.setVisible(false);
        }
        
        
        if (ae.getActionCommand().equalsIgnoreCase("PLAY")) {
            
            _myController.play();
            
        } else if (ae.getActionCommand().equalsIgnoreCase("PAUSE")) {
            
            _myController.pause();
            
        } else if (ae.getActionCommand().equalsIgnoreCase("STOP")) {
            
            _myController.stop();
            
        } else if (ae.getActionCommand().equalsIgnoreCase("PREVIOUS")) {
            
            try {
                _myController.previous();
            } catch (UnsupportedAudioFileException ex) {
                Logger.getLogger(MultimediaView.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IOException ex) {
                Logger.getLogger(MultimediaView.class.getName()).log(Level.SEVERE, null, ex);
            }
            
        } else if (ae.getActionCommand().equalsIgnoreCase("NEXT")) {
            
            try {
                _myController.next();
            } catch (UnsupportedAudioFileException ex) {
                Logger.getLogger(MultimediaView.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IOException ex) {
                Logger.getLogger(MultimediaView.class.getName()).log(Level.SEVERE, null, ex);
            }
            
        } else if (ae.getActionCommand().equalsIgnoreCase("SHUFFLE")) {
            
            boolean active = _myController.getModel().isShuffle();
            _myController.setShuffle(!active);
            
        } else if (ae.getActionCommand().equalsIgnoreCase("REPEAT")) {
            
            boolean active = _myController.getModel().isRepeat();
            _myController.setRepeat(!active);
            
        } else if (ae.getActionCommand().equalsIgnoreCase("ADD")) {

            File file = null;
            try {
                file = new File(((FileModel) _myTree.getSelectionPath().getLastPathComponent()).getPath());
            } catch (NullPointerException ex) {
                return;
            }

            try {
                _myController.addSongToActivePlaylist(file.getAbsolutePath());

            } catch (FileNotFoundException ex) {
                this.showFileNotFoundException(file.getAbsolutePath());
            } catch (IOException ex) {
                this.showDevErrorBox(ex);
            } catch (UnsupportedAudioFileException ex) {
                this.showDevErrorBox(ex);
            }
        }
        
        updateAll();
    }

    @Override
    public void valueChanged(TreeSelectionEvent e) {
    }

    @Override
    public void mouseClicked(MouseEvent me) {

        if (me.getSource() != _myTree) {
            return;
        }
        
        // Get the selected file or return if nothing is selected
        File file = null;
        try {
            file = new File(((FileModel) _myTree.getSelectionPath().getLastPathComponent()).getPath());
        } catch (NullPointerException ex) {
            return;
        }

        // On double click, we play a song alone
        if (me.getClickCount() == 2) {

            launchNewSong(file.getAbsolutePath());
        }

    }

    @Override
    public void mousePressed(MouseEvent me) {
        
        if (me.getSource() != _myTree) {
            return;
        }
        
        if (me.getButton() == MouseEvent.BUTTON3) {
            _rightClick.setLocation(me.getXOnScreen(), me.getYOnScreen());
            _rightClick.build();
            _rightClick.setVisible(true);
        }

        if (me.getClickCount() == 1 && me.getButton() == MouseEvent.BUTTON1) {
            _rightClick.setVisible(false);            
        }
    }

    @Override
    public void mouseReleased(MouseEvent me) {
    }

    @Override
    public void mouseEntered(MouseEvent me) {
    }

    @Override
    public void mouseExited(MouseEvent me) {
    }
}